package com.yc.system.service.impl;

import com.yc.common.constant.Constants;
import com.yc.common.constant.HttpStatus;
import com.yc.common.core.domain.model.MathCaptcha;
import com.yc.common.enums.CaptchaType;
import com.yc.common.exceptions.ServiceException;
import com.yc.common.exceptions.captcha.CaptchaException;
import com.yc.common.exceptions.captcha.CaptchaExpireException;
import com.yc.framework.manager.AsyncManager;
import com.yc.framework.manager.factory.AsyncFactory;
import com.yc.system.service.CaptchaService;
import com.yc.common.utils.MathExpressionGenerator;
import com.yc.common.core.redis.RedisCache;
import com.yc.system.service.EmailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Service
public class CaptchaServiceImpl implements CaptchaService {

    // 字体列表
    private static final String[] FONTS = {"Arial", "Verdana", "Tahoma", "Georgia"};

    // 图片验证码缓存
    private ConcurrentHashMap<String, BufferedImage> captchaImageCache = new ConcurrentHashMap<>();

    // 数学表达式生成器
    private final MathExpressionGenerator expressionGenerator;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private EmailService emailService;

    public CaptchaServiceImpl() {
        // 创建表达式生成器，结果范围为20-120
        this.expressionGenerator = new MathExpressionGenerator(20, 120);
    }

    @Override
    public String createCaptcha() {
        // 生成验证码ID
        String captchaId = UUID.randomUUID().toString();

        // 生成数学表达式
        MathExpressionGenerator.Result result = expressionGenerator.generate();

        // 创建验证码对象
        MathCaptcha captcha = new MathCaptcha(captchaId, result.getExpression(), result.getResultAsString());

        // 生成验证码图片
        BufferedImage captchaImage = generateCaptchaImage(captcha.getExpression());

        // 将验证码结果存入Redis
        String captchaKey = getCaptchaKey(captcha.getCaptchaId());
        redisCache.setCacheObject(captchaKey, captcha.getResult(),
                Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);

        // 将验证码图片缓存起来
        captchaImageCache.put(captcha.getCaptchaId(), captchaImage);

        // 返回验证码ID
        return captcha.getCaptchaId();
    }

    @Override
    public BufferedImage getCaptchaImage(String captchaId) {
        return captchaImageCache.get(captchaId);
    }

    /**
     * @Author YanChen
     * @Description 校验验证码 （登录、注册）
     * @Date 19:31 2025/4/13
     **/
    @Override
    public void validateCaptcha(String phone, String captchaId, String userInput) {
        // 判断传来的验证码id是否存在
        if (captchaId == null || captchaId.isEmpty()) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(
                    phone, Constants.LOGIN_FAIL, "验证码错误"));
            throw new CaptchaException("验证码错误，请重试");
        }

        // 用户输入的结果为空，错误
        if (userInput == null || userInput.isEmpty()) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(
                    phone, Constants.LOGIN_FAIL, "验证码不能为空"));
            throw new CaptchaException("验证码不能为空");
        }

        // 从Redis获取正确结果
        String captchaKey = getCaptchaKey(captchaId);
        String correctResult = redisCache.getCacheObject(captchaKey);

        // Redis中的验证码是否过期或为空
        if (correctResult == null || correctResult.isEmpty()) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(
                    phone, Constants.LOGIN_FAIL, "验证码已失效，请重试"));
            throw new CaptchaExpireException("验证码已失效，请重新获取");
        }

        // 验证码不匹配
        if (!correctResult.equals(userInput)) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(
                    phone, Constants.LOGIN_FAIL, "验证码错误"));
            throw new CaptchaException("验证码错误，请重试");
        }

        // 验证通过，删除Redis中的验证码和图片缓存
        redisCache.deleteObject(captchaKey);
        captchaImageCache.remove(captchaId);
    }

    /**
     * 生成并发送验证码到用户邮箱
     *
     * @param email 用户邮箱
     * @return 验证码ID
     */
    @Override
    public String createAndSendEmailCaptcha(String email) {
        // 生成验证码ID
        String captchaId = UUID.randomUUID().toString();

        // 生成数学表达式
        Random random = new Random();
        StringBuilder code = new StringBuilder();

        for (int i = 0; i < 6; i++) {
            code.append(random.nextInt(10));
        }
        try {
            // 生成验证码图片
            BufferedImage captchaImage = generateCaptchaImage(String.valueOf(code));

            // 将验证码结果存入Redis
            String captchaKey = getCaptchaKey(captchaId);
            redisCache.setCacheObject(captchaKey, code,
                    Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);

            // 将验证码图片缓存起来
            captchaImageCache.put(CaptchaType.EMAIL.getDesc(), captchaImage);

            // 异步发送验证码到用户邮箱
            emailService.sendCaptchaEmailAsync(email, "验证码", captchaImage);

            // 返回验证码ID
            return captchaId;
        } catch (CaptchaException e) {
            throw new CaptchaException();
        }
    }

    /**
     * @Author YanChen
     * @Description 校验验证码 （忘记密码）
     * @Date 1:16 2025/4/25
     **/
    @Override
    public void validateCaptchaForgetPassword(String email, String captchaId, String code) {
        // 判断传来的验证码id是否存在
        if (captchaId == null || captchaId.isEmpty()) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(
                    email, Constants.LOGIN_FAIL, "验证码错误"));
            throw new CaptchaException("验证码错误，请重试");
        }

        // 用户输入的结果为空，错误
        if (code == null || code.isEmpty()) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(
                    email, Constants.LOGIN_FAIL, "验证码不能为空"));
            throw new CaptchaException("验证码不能为空");
        }

        // 从Redis获取正确结果
        String captchaKey = getCaptchaKey(captchaId);
        String correctResult = redisCache.getCacheObject(captchaKey);

        // Redis中的验证码是否过期或为空
        if (correctResult == null || correctResult.isEmpty()) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(
                    email, Constants.LOGIN_FAIL, "验证码已失效，请重试"));
            throw new CaptchaExpireException("验证码已失效，请重新获取");
        }

        // 验证码不匹配
        if (!correctResult.equals(code)) {
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(
                    email, Constants.LOGIN_FAIL, "验证码错误"));
            throw new CaptchaException("验证码错误，请重试");
        }

        // 验证通过，删除Redis中的图片缓存
        captchaImageCache.remove(CaptchaType.EMAIL.getDesc());
    }

    /**
     * 生成验证码图片
     *
     * @param expression 数学表达式
     * @return 验证码图片
     */
    private BufferedImage generateCaptchaImage(String expression) {
        // 修改图片尺寸，适当增加宽度以确保完整显示表达式
        int width = 140;  // 从120px增加到140px
        int height = 45;  // 从40px增加到45px

        // 创建图片缓冲区
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = image.createGraphics();

        // 设置抗锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        // 设置背景色
        g2d.setColor(new Color(250, 250, 255));
        g2d.fillRect(0, 0, width, height);

        // 绘制干扰线（减少数量）
        drawInterferenceLines(g2d, width, height, 3);

        // 绘制表达式
        drawExpression(g2d, expression, width, height);

        // 释放资源
        g2d.dispose();

        return image;
    }

    /**
     * 绘制干扰线
     */
    private void drawInterferenceLines(Graphics2D g2d, int width, int height, int lineCount) {
        Random random = new Random();
        g2d.setColor(new Color(200, 200, 200, 60)); // 更淡的干扰线

        for (int i = 0; i < lineCount; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            g2d.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * 绘制表达式
     */
    private void drawExpression(Graphics2D g2d, String expression, int width, int height) {
        Random random = new Random();
        g2d.setColor(new Color(0, 0, 100)); // 深蓝色

        // 使用更合适的字体大小
        String fontName = FONTS[random.nextInt(FONTS.length)];
        Font font = new Font(fontName, Font.BOLD, 22);  // 字体大小从20增加到22
        g2d.setFont(font);

        // 计算文本宽度以便居中
        FontMetrics metrics = g2d.getFontMetrics(font);
        int textWidth = metrics.stringWidth(expression);

        // 如果表达式太长，减小字体
        if (textWidth > width - 20) {  // 留出更多边距
            font = new Font(fontName, Font.BOLD, 20);
            g2d.setFont(font);
            metrics = g2d.getFontMetrics(font);
            textWidth = metrics.stringWidth(expression);
        }

        int x = (width - textWidth) / 2;
        int y = (height + metrics.getHeight()) / 2;

        // 绘制表达式，字符没有旋转，提高可读性
        g2d.drawString(expression, x, y);
    }

    /**
     * 获取验证码Redis键
     */
    private String getCaptchaKey(String captchaId) {
        return "captcha:" + captchaId;
    }
}